Skip to content

rebase#91

Merged
EthanThePhoenix38 merged 49 commits intoalert-autofix-21from
main
Feb 14, 2026
Merged

rebase#91
EthanThePhoenix38 merged 49 commits intoalert-autofix-21from
main

Conversation

@EthanThePhoenix38
Copy link
Member

@EthanThePhoenix38 EthanThePhoenix38 commented Feb 14, 2026


Continue Tasks: ▶️ 1 queued — View all

Copilot AI and others added 30 commits February 5, 2026 19:03
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
…ting

Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Co-authored-by: EthanThePhoenix38 <103653068+EthanThePhoenix38@users.noreply.github.com>
Bumps [openai](https://github.com/openai/openai-node) from 6.17.0 to 6.19.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](openai/openai-node@v6.17.0...v6.19.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 6.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@csstools/css-syntax-patches-for-csstree](https://github.com/csstools/postcss-plugins/tree/HEAD/packages/css-syntax-patches-for-csstree) from 1.0.26 to 1.0.27.
- [Changelog](https://github.com/csstools/postcss-plugins/blob/main/packages/css-syntax-patches-for-csstree/CHANGELOG.md)
- [Commits](https://github.com/csstools/postcss-plugins/commits/HEAD/packages/css-syntax-patches-for-csstree)

---
updated-dependencies:
- dependency-name: "@csstools/css-syntax-patches-for-csstree"
  dependency-version: 1.0.27
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…/css-syntax-patches-for-csstree-1.0.27' into securite
Bumps [@csstools/css-calc](https://github.com/csstools/postcss-plugins/tree/HEAD/packages/css-calc) from 3.0.0 to 3.0.1.
- [Changelog](https://github.com/csstools/postcss-plugins/blob/main/packages/css-calc/CHANGELOG.md)
- [Commits](https://github.com/csstools/postcss-plugins/commits/HEAD/packages/css-calc)

---
updated-dependencies:
- dependency-name: "@csstools/css-calc"
  dependency-version: 3.0.1
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 11.2.5 to 11.2.6.
- [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md)
- [Commits](isaacs/node-lru-cache@v11.2.5...v11.2.6)

---
updated-dependencies:
- dependency-name: lru-cache
  dependency-version: 11.2.6
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@exodus/bytes](https://github.com/ExodusOSS/bytes) from 1.12.0 to 1.14.0.
- [Release notes](https://github.com/ExodusOSS/bytes/releases)
- [Commits](ExodusOSS/bytes@v1.12.0...v1.14.0)

---
updated-dependencies:
- dependency-name: "@exodus/bytes"
  dependency-version: 1.14.0
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
dependabot bot and others added 18 commits February 12, 2026 06:17
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](ljharb/qs@v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the npm_and_yarn group with 1 update in the / directory: [qs](https://github.com/ljharb/qs).


Updates `qs` from 6.14.1 to 6.14.2
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](ljharb/qs@v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
The labeler workflow was completing with `action_required` status
instead of success, blocking PR checks.

## Changes

- Added `issues: write` permission to `.github/workflows/label.yml`

The labeler action requires this permission to manage labels on pull
requests. Without it, the workflow completes but GitHub marks it as
requiring action.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
The digest emails reference `template=unsubscribe.yml` in unsubscribe
links (line 438 of `src/aggregator.js`), but the template file didn't
exist. Users clicking "Se désabonner" were sent to a 404.

**Changes:**
- Added `.github/ISSUE_TEMPLATE/unsubscribe.yml` with required email
field and optional reason field
- Follows existing bilingual (French/English) pattern from
`subscribe.yml`

The unsubscribe flow is now functional end-to-end.

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/ThePhoenixAgency/AI-Pulse/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
The error handler for missing `franc-min` logged "franc-min not
available, using feed-declared language only" without telling users how
to fix it.

## Changes

- Enhanced error message to include actionable resolution steps
- Message now explicitly instructs users to run `npm install` when the
package is missing

```javascript
} catch (e) {
  console.error('WARNING: franc-min package not found. Language detection will be limited to feed-declared languages only.');
  console.error('To enable automatic language detection, please run: npm install');
  detectLang = () => null;
}
```

This addresses the review feedback that error messages should be
informative and include instructions for resolution.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
…tibility (#61)

Addresses all actionable review feedback from PR #57 focusing on i18n
quality, input validation, error handling, and browser compatibility.

## French Localization
- Fixed missing accents throughout: `Français`, `Cybersécurité`,
`Générale`, `préférée`, `Fréquence`, `Réinitialiser`, etc.
- Updated config.json, issue templates, email templates, and UI strings

## Input Validation & Security

**Aggregator (`src/aggregator.js`)**
- Config loading with actionable error messages on missing/malformed
files
- Email validation (RFC-compliant regex) before sending
- API key format validation with sanitized error output
- Deduplication threshold type checking
- HTML lang attribute validation (ISO 639-1 codes only)

**Workflows**
- Email validation with abuse detection (max @ symbol threshold)
- RSS URL format validation
- Input sanitization for source names and tags (XSS prevention)

```javascript
// Before: silent crash on missing config
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));

// After: actionable error
try {
  if (!fs.existsSync(CONFIG_PATH)) {
    console.error(`ERROR: Configuration file not found at ${CONFIG_PATH}`);
    console.error('Please ensure config.json exists in the repository root.');
    process.exit(1);
  }
  // ...
} catch (e) {
  console.error(`ERROR: Failed to load or parse config.json: ${e.message}`);
  process.exit(1);
}
```

## Browser Compatibility
- `localStorage` availability checks before all operations
- Try-catch wrappers with quota overflow recovery
- `IntersectionObserver` feature detection with graceful degradation
- `ReadHistory.markRead` safety guard against undefined

## Rate Limiting & Configuration
- Email sending rate limit (100ms delay between sends)
- Configurable sender via `EMAIL_FROM` env var with dev domain warning
- Constants extracted for magic numbers (`VALID_LANG_CODES`,
`MAX_SOURCE_NAME_LENGTH`, etc.)

## Security Scan
✅ CodeQL: 0 alerts

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---

<!-- continue-task-summary-start -->
**Continue Tasks:** ▶️ 1 queued — [View
all](https://hub.continue.dev/inbox?pr=https%3A%2F%2Fgithub.com%2FThePhoenixAgency%2FAI-Pulse%2Fpull%2F88&utm_source=github_pr&utm_medium=pr_body&utm_campaign=continue_tasks)
<!-- continue-task-summary-end -->
---

<!-- continue-task-summary-start -->
**Continue Tasks:** ▶️ 1 queued — [View
all](https://hub.continue.dev/inbox?pr=https%3A%2F%2Fgithub.com%2FThePhoenixAgency%2FAI-Pulse%2Fpull%2F89&utm_source=github_pr&utm_medium=pr_body&utm_campaign=continue_tasks)
<!-- continue-task-summary-end -->
@EthanThePhoenix38 EthanThePhoenix38 marked this pull request as ready for review February 14, 2026 12:58
Copilot AI review requested due to automatic review settings February 14, 2026 12:58
@EthanThePhoenix38 EthanThePhoenix38 merged commit 479ae60 into alert-autofix-21 Feb 14, 2026
13 checks passed
EthanThePhoenix38 added a commit that referenced this pull request Feb 14, 2026
---

<!-- continue-task-summary-start -->
**Continue Tasks:** ▶️ 1 queued — [View
all](https://hub.continue.dev/inbox?pr=https%3A%2F%2Fgithub.com%2FThePhoenixAgency%2FAI-Pulse%2Fpull%2F92&utm_source=github_pr&utm_medium=pr_body&utm_campaign=continue_tasks)
<!-- continue-task-summary-end -->
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR appears to be a “rebase”-style update that mixes French copy fixes with operational hardening across the Node aggregator, GitHub issue-based automation, and the in-browser reader UI.

Changes:

  • Improved robustness in src/aggregator.js (config load error handling, email sending validation/rate limiting, language validation scaffolding).
  • Hardened GitHub Actions workflows that parse issue forms (added validation/sanitization; updated labels with accents).
  • Added localStorage availability checks and some UI text fixes in the reader UI.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
templates/email-digest.html Fixes French accents in email template copy.
src/aggregator.js Adds config load guards + email digest improvements; introduces language validation and refactors dedupe.
readme-viewer.html Adds localStorage availability checks; updates French copy; introduces scroll-spy code.
package.json Bumps openai dependency version.
package-lock.json Updates lockfile for dependency bumps.
js/tracker.js Adds localStorage availability checks and better error handling around tracking.
config.json Fixes French accents in category/tag labels.
CHANGELOG.md Appends automated security update entries across multiple versions.
.github/workflows/manage-subscriber.yml Adds stricter email validation/abuse heuristic; updates labels with accents.
.github/workflows/label.yml Adds issues: write permission for labeler workflow.
.github/workflows/add-source.yml Adds validation/sanitization for source proposals; updates category label text.
.github/ISSUE_TEMPLATE/unsubscribe.yml Adds a new unsubscribe issue form.
.github/ISSUE_TEMPLATE/subscribe.yml Updates labels/options with accents.
.github/ISSUE_TEMPLATE/new-source.yml Updates labels/options with accents.
Comments suppressed due to low confidence (2)

.github/workflows/manage-subscriber.yml:96

  • Category mapping mismatch: the subscribe issue form uses the option label "Tech Generale" (no accent) but the workflow expects "Tech Générale". If a user selects only that category, it won’t map and they’ll be subscribed to all categories due to the fallback. Align the option label in .github/ISSUE_TEMPLATE/subscribe.yml with the keys in catMap, or accept both spellings in the mapping.
            const catMap = {
              'AI / IA': 'ai',
              'Cybersecurity': 'cybersecurity',
              'IoT': 'iot',
              'Windows': 'windows',
              'Mac / Apple': 'mac',
              'Linux': 'linux',
              'Tech Générale': 'tech',
              'Entrepreneuriat': 'entrepreneurship',
              'Bourse & Finance': 'finance',
              'Crypto & Blockchain': 'crypto',
              'Open Source & GitHub': 'opensource',

src/aggregator.js:477

  • deduplicateArticles() now computes threshold/normalizedTitles but the loop still references normalizedData, titleThreshold, and contentThreshold, which are no longer defined. This will throw at runtime and break aggregation. Either restore the original titleThreshold/contentThreshold + normalizedData variables, or refactor the comparison logic to consistently use the new threshold and the new normalized cache array.
  const threshold = (typeof SETTINGS.deduplication.similarityThreshold === 'number' && 
                     SETTINGS.deduplication.similarityThreshold > 0 && 
                     SETTINGS.deduplication.similarityThreshold <= 1) 
                    ? SETTINGS.deduplication.similarityThreshold 
                    : 0.7;
  const kept = [];
  const normalizedTitles = [];

  for (const article of articles) {
    // Normaliser le titre et le résumé de l'article courant
    const titleWords = normalizeText(article.title);
    const contentWords = normalizeText(article.summary || '');
    let isDuplicate = false;

    // Comparer avec chaque article déjà gardé
    for (let i = 0; i < normalizedData.length; i++) {
      const titleSim = textSimilarity(titleWords, normalizedData[i].title);
      const contentSim = textSimilarity(contentWords, normalizedData[i].content);

      // Conditions pour être considéré comme doublon:
      // 1. Titre très similaire (70%+)
      // 2. OU contenu très similaire (50%+)
      // 3. OU les deux modérément similaires (40%+ chacun)
      if (titleSim >= titleThreshold ||
          contentSim >= contentThreshold ||
          (titleSim >= 0.4 && contentSim >= 0.4)) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


// Map language
const lang = languageRaw.includes('Francais') ? 'fr' : 'en';
const lang = languageRaw.includes('Français') ? 'fr' : 'en';
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Language mapping will always fall back to 'en' because it checks languageRaw.includes('Français'), but the issue form options are "Francais / French", "Anglais / English", etc. Update the check to handle the actual option text (e.g., match both Francais and Français, or map explicit option values).

Suggested change
const lang = languageRaw.includes('Français') ? 'fr' : 'en';
const normalizedLanguage = (languageRaw || '').toLowerCase();
const isFrench = normalizedLanguage.includes('francais') || normalizedLanguage.includes('français');
const lang = isFrench ? 'fr' : 'en';

Copilot uses AI. Check for mistakes.
Comment on lines 8 to +19
## [1.1.0] - 2026-01-31
### Added
- **2026-02-14 11:26 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-13 06:16 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-13 06:15 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-12 06:17 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-12 06:16 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-11 06:17 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-11 06:16 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-11 06:16 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-10 06:16 UTC**: Automated Security Batch Update (DependabotSecureFlow)
- **2026-02-10 06:16 UTC**: Automated Security Batch Update (DependabotSecureFlow)
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changelog entries appear to be duplicated across multiple released versions and include timestamps after the stated release date (e.g., entries dated 2026-02-14 under [1.1.0] - 2026-01-31). This makes the changelog misleading. Consider moving these automated security-update lines to an [Unreleased] section or the correct version, and avoid repeating the same items under multiple releases.

Copilot uses AI. Check for mistakes.
Comment on lines 533 to 621
@@ -613,16 +610,16 @@ async function processArticle(article, sourceName, tags, category, feedLang) {

// Retourner l'objet article traité
return {
title: (sanitizeText(article.title) || 'Untitled').slice(0, 200), // Titre limité à 200 caractères
link: finalReaderLink, // Lien vers la version locale ou originale
originalLink: articleUrl, // Lien original (toujours gardé)
pubDate: new Date(article.pubDate || Date.now()), // Date de publication
source: sourceName, // Nom de la source
tags: tags, // Tags de la source
category: category, // Catégorie
lang: detectedLang || feedLang || 'en', // Langue détectée ou déclarée
summary: smartTruncate(rawSummary), // Résumé tronqué intelligemment
hasLocalContent: hasLocalContent // Indique si on a une copie locale
title: (sanitizeText(article.title) || 'Untitled').slice(0, 200),
link: finalReaderLink,
originalLink: articleUrl, // Always set to ensure RSS feeds have valid external URLs
pubDate: new Date(article.pubDate || Date.now()),
source: sourceName,
tags: tags,
category: category,
lang: detectedLang || feedLang || 'en',
summary: smartTruncate(rawSummary),
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Language validation is currently ineffective/inconsistent: lang is computed once (after an initial validation), but detectedLang may be reassigned later from full-article text without re-validation, and the returned article uses detectedLang || feedLang instead of the validated lang. Recompute/validate the final language after all detection steps and use that same value both in the generated HTML (<html lang=...>) and the returned article.lang.

Copilot uses AI. Check for mistakes.
@@ -481,11 +481,7 @@ <h4>Articles par section</h4>
<span class="range-value" id="prefMaxArticlesValue">30</span>
</div>

Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preferences UI removed the prefHideRead checkbox, but the script still does document.getElementById('prefHideRead')... in setupPrefsPanel(). This will throw (null dereference) and break the preferences panel. Either re-add the checkbox markup, or remove the prefHideRead event wiring and the hideRead preference if it’s no longer supported.

Suggested change
<div class="prefs-section">
<h4>Articles lus</h4>
<label>
<input type="checkbox" id="prefHideRead" name="prefHideRead">
Masquer les articles déjà lus
</label>
</div>

Copilot uses AI. Check for mistakes.
Comment on lines 783 to 806
});

// Rebuild section dropdown when filters change
buildSectionNav();
// ─── Intersection Observer for active section ───────
function setupScrollSpy() {
// Check if IntersectionObserver is supported
if (!('IntersectionObserver' in window)) {
console.warn('IntersectionObserver not supported. Scroll spy disabled.');
return;
}

const sections = document.querySelectorAll('#readme-content section[data-category]');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const cat = entry.target.getAttribute('data-category');
document.querySelectorAll('.section-nav a').forEach(a => a.classList.remove('active'));
const activeLink = document.querySelector(`.section-nav a[data-nav-cat="${cat}"]`);
if (activeLink) activeLink.classList.add('active');
}
});
}, { rootMargin: '-20% 0px -60% 0px' });

sections.forEach(section => observer.observe(section));
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyFilters() is missing its closing brace before the new setupScrollSpy() declaration, which nests setupScrollSpy, setupPrefsPanel, and loadReadme inside applyFilters and leaves braces unbalanced. This will prevent the script from running correctly (and may cause a syntax error). Close applyFilters() before declaring the other functions, and call setupScrollSpy() after rendering (e.g., in the post-render setup of loadReadme()).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants